package tetris;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.sql.SQLOutput;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * 俄罗斯方块游戏场景类
 *
 * @author Leslie Leung
 */
public class TetrisPane extends JPanel {
    public static final int ROWS = 20;    //整个场景的行数
    public static final int COLUMNS = 10;    //整个场景的列数

    /* 表示7种不同的四格方块 */
    public static final int I_SHAPED = 0;
    public static final int S_SHAPED = 1;
    public static final int T_SHAPED = 2;
    public static final int Z_SHAPED = 3;
    public static final int L_SHAPED = 4;
    public static final int O_SHAPED = 5;
    public static final int J_SHAPED = 6;

    public static final int KIND = 7;    //表示四格方块有7个种类
    public static final int INIT_SPEED = 1000;    //表示下落的初始速度

    private static int randomNum = 0;    //表示已生成的俄罗斯方块的数目

    private Random random;
    private Tetromino currentTetromino;    //表示当前的四格方块
    private Cell[][] wall;        //表示墙，null表示方块内没对象
    private Timer autoDrop;        //实现自动下落的计时器
    private KeyControl keyListener;    //表示键盘事件监控变量
    private boolean isPause;  //是否暂停
    private int[][] saveData;  //记录数据
    private boolean flag;
    private int totaval;
    private int[][] spGameData; //记录游戏时的稀疏数组

    /**
     * 构造方法
     */
    public TetrisPane() {
        setPreferredSize(new Dimension(COLUMNS * Cell.CELL_SIZE, ROWS * Cell.CELL_SIZE));

        random = new Random();
        wall = new Cell[ROWS][COLUMNS];
        saveData = new int[ROWS][COLUMNS];
        autoDrop = new Timer();
        keyListener = new KeyControl();
        randomOne();

        autoDrop.schedule(new DownTask(), 1000, 650);
    }

    /**
     * 随机生成一个四格方块
     */
    public void randomOne() {
        Tetromino tetromino = null;
        int ai = random.nextInt(7);
        switch (ai) {
            case 0:
                tetromino = new JShaped();
                break;
            case 1:
                tetromino = new LShaped();
                break;
            case 2:
                tetromino = new ZShaped();
                break;
            case 3:
                tetromino = new SShaped();
                break;
            case 4:
                tetromino = new TShaped();
                break;
            case 5:
                tetromino = new IShaped();
                break;
            case 6:
                tetromino = new OShaped();
                break;

        }
        currentTetromino = tetromino;
        randomNum++;

    }


    /**
     * 判断玩家是否输了
     *
     * @return true，输了；false，没输
     */
    public boolean isGameOver() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            int y = getCurrentCells()[i].getY();
            int x = getCurrentCells()[i].getX();
            if (isHaveOther(x, y)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 每生成一个俄罗斯方块，通过改变TimerTask的时间间隔来加快下落速度
     *
     * @return 时间间隔
     */
    public double interval() {
        return INIT_SPEED * Math.pow((double) 39 / 38, 0 - randomNum);
    }

    /**
     * 返回KeyControl类的实例
     *
     * @return KeyControl类实例
     */
    public KeyControl getInnerInstanceOfKeyControl() {
        return keyListener;
    }

    /**
     * 内部类，用于实现俄罗斯方块的自动下落
     * @author Leslie Leung
     */


    /**
     * 把俄罗斯方块添加到墙上
     */


    /**
     * 内部类，用于实现键盘的事件控制
     *
     * @author Leslie Leung
     */
    private class KeyControl extends KeyAdapter {

        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_UP://顺时针转
                    if (isPause) {
                        break;
                    }
                    if (!isClockWiseRotateOut() && !(currentTetromino instanceof OShaped)) {
                        currentTetromino.clockwiseRotate(getAxis(), getRotateCells());
                        repaint();
                    }
                    break;
                case KeyEvent.VK_D:    //逆时针转
                    if (isPause) {
                        break;
                    }
                    if (!isAntiClockWiseRotateOut() && !(currentTetromino instanceof OShaped)) {
                        currentTetromino.anticlockwiseRotate(getAxis(), getRotateCells());
						repaint();
                    }
                    break;
                case KeyEvent.VK_LEFT:
                    if (isPause) {
                        break;
                    }
                    if (!isReachLeftEdge()) {
                        currentTetromino.moveLeft();
                        repaint();
                    }
                    break;
                case KeyEvent.VK_RIGHT:
                    if (isPause) {
                        break;
                    }
                    if (!isReachRightEdge()) {
                        currentTetromino.moveRight();
                        repaint();
                    }
                    break;
                case KeyEvent.VK_DOWN:
                    if (isPause) {
                        break;
                    }
                    if (!isReachBottomEdge()) {
                        currentTetromino.moveDown();
                        repaint();
                    }
                    break;
                case KeyEvent.VK_SPACE:
                    if (isPause) {
                        break;
                    }
                    while (!isReachBottomEdge()) {
                        currentTetromino.moveDown();
                    }
                    repaint();
                    break;
                case KeyEvent.VK_P:
                    isPause = !isPause;
                    saveGame();
                    saveToSparseArray();
                    try {
                        saveToDisk();
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }
                    break;
            }
        }
    }
    //将稀疏数组存储到磁盘
    private void saveToDisk() throws Exception {
        File file = new File("f:/sp.txt");
        OutputStream out = new FileOutputStream(file);
        for (int i = 0; i < spGameData.length; i++) {
            for (int j = 0; j < spGameData[i].length; j++) {
                out.write(spGameData[i][j]);
            }

        }
        out.close();
    }

    //将游戏数据存入到稀疏数组
    private void saveToSparseArray() {
        spGameData = new int[totaval + 1][3];
        spGameData[0][0] = ROWS;
        spGameData[0][1] = COLUMNS;
        spGameData[0][2] = totaval;
        totaval = 0;
        int count = 0;
        for (int i = 0; i < saveData.length; i++) {
            for (int j = 0; j < saveData[i].length; j++) {
                if (saveData[i][j] != 0) {
                    count++;
                    spGameData[count][0] = i;
                    spGameData[count][1] = j;
                    spGameData[count][2] = saveData[i][j];
                }
            }
        }
    }

    //记录当前游戏状态
    private void saveGame() {
        for (int i = 0; i < wall.length; i++) {
            for (int j = 0; j < wall[i].length; j++) {
                if (wall[i][j] == null) {
                    saveData[i][j] = 0;
                } else if (wall[i][j].color == Cell.COLOR_CYAN) {
                    saveData[i][j] = 6;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_BLUE) {
                    saveData[i][j] = 5;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_GREEN) {
                    saveData[i][j] = 7;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_ORANGE) {
                    saveData[i][j] = 4;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_PINK) {
                    saveData[i][j] = 3;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_RED) {
                    saveData[i][j] = 1;
                    totaval++;
                } else if (wall[i][j].color == Cell.COLOR_YELLOW) {
                    saveData[i][j] = 2;
                    totaval++;
                }
            }
        }
    }

    //判断旋转是否可以执行，return true，表示越界,false 没有越界
    public boolean isAntiClockWiseRotateOut() {
        for (int i = 0; i < 3; i++) {
            int oldX = getCurrentCells()[i].getX();
            int oldY = getCurrentCells()[i].getY();
            int newX = getAxis().getX() - getAxis().getY() + oldY;
            int newY = getAxis().getX() + getAxis().getX() - oldX;
            if (newX < 0 || newX > COLUMNS - 1 || newY < 0 || newY > ROWS - 1) {
                return true;
            }
            if (isHaveOther(newX, newY)) {
                return true;
            }
        }
        return false;

    }

    //判断旋转是否可以执行，return true，表示越界,false 没有越界
    public boolean isClockWiseRotateOut() {
        for (int i = 0; i < 3; i++) {
            int oldX = getCurrentCells()[i].getX();
            int oldY = getCurrentCells()[i].getY();
            int newX = getAxis().getX() - oldY + getAxis().getY();
            int newY = getAxis().getY() + oldX - getAxis().getX();
            if (newX < 0 || newX > COLUMNS - 1 || newY < 0 || newY > ROWS - 1) {
                return true;
            }
            if (isHaveOther(newX, newY)) {
                return true;
            }
        }
        return false;
    }

    //创建定时器类
    private class DownTask extends TimerTask {
        @Override
        public void run() {
            if (!isPause) {
                if (isGameOver()) {
                    removeKeyListener(keyListener);
                    autoDrop.cancel();
                    JOptionPane.showMessageDialog(null, "牛马！你输了！");
                    return;
                }
                if (!isReachBottomEdge()) {
                    currentTetromino.moveDown();
                } else {
                    saveToWall();
                    veryNbClearRow();
                    randomOne();
                    autoDrop.cancel();
                    autoDrop = new Timer();
                    autoDrop.schedule(new DownTask(), (long) interval(), (long) interval());
                }
                repaint();
            }

        }
    }

    //删除所有需要删除的行
    public void veryNbClearRow() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            clearRow(getCurrentCells()[i].getY());
        }
    }

    //判断方块下方是否已存在其他方块
    public boolean isHaveOther(int x, int y) {
        if (wall[y][x] == null) {
            return false;
        } else {
            return true;
        }
    }

    //将俄罗斯方块储存到二维数组中
    public void saveToWall() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            int x = getCurrentCells()[i].getX();
            int y = getCurrentCells()[i].getY();
            wall[y][x] = getCurrentCells()[i];
        }
    }

    //判断方块是否到达游戏窗口的左边界
    public boolean isReachLeftEdge() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            int oldX = getCurrentCells()[i].getX();
            int oldY = getCurrentCells()[i].getY();
            int newX = oldX - 1;
            if (oldX == 0) {
                return true;
            }
            if (isHaveOther(newX, oldY)) {
                return true;
            }
        }

        return false;
    }

    //判断方块是否到达游戏窗口的右边界
    public boolean isReachRightEdge() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            int oldX = getCurrentCells()[i].getX();
            int newX = oldX + 1;
            int oldY = getCurrentCells()[i].getY();
            if (oldX == COLUMNS - 1) {
                return true;
            }
            if (isHaveOther(newX, oldY)) {
                return true;
            }
        }
        return false;
    }

    //判断方块是否到达游戏窗口的下边界
    public boolean isReachBottomEdge() {
        for (int i = 0; i < getCurrentCells().length; i++) {
            int oldY = getCurrentCells()[i].getY();
            int oldX = getCurrentCells()[i].getX();
            int newY = oldY + 1;
            if (oldY == ROWS - 1) {
                return true;
            }
            if (isHaveOther(oldX, newY)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 内部类，I形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class IShaped extends Tetromino {
        /**
         * 构造方法
         */
        public IShaped() {
            cells = new Cell[4];

            cells[1] = new Cell(3, 0, Cell.COLOR_CYAN);
            cells[0] = new Cell(4, 0, Cell.COLOR_CYAN);
            cells[2] = new Cell(5, 0, Cell.COLOR_CYAN);
            cells[3] = new Cell(6, 0, Cell.COLOR_CYAN);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，S形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class SShaped extends Tetromino {
        /**
         * 构造方法
         */
        public SShaped() {
            cells = new Cell[4];

            cells[0] = new Cell(4, 0, Cell.COLOR_BLUE);
            cells[1] = new Cell(5, 0, Cell.COLOR_BLUE);
            cells[2] = new Cell(3, 1, Cell.COLOR_BLUE);
            cells[3] = new Cell(4, 1, Cell.COLOR_BLUE);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，T形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class TShaped extends Tetromino {
        /**
         * 构造方法
         */
        public TShaped() {
            cells = new Cell[4];

            cells[1] = new Cell(3, 0, Cell.COLOR_GREEN);
            cells[0] = new Cell(4, 0, Cell.COLOR_GREEN);
            cells[2] = new Cell(5, 0, Cell.COLOR_GREEN);
            cells[3] = new Cell(4, 1, Cell.COLOR_GREEN);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，Z形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class ZShaped extends Tetromino {
        /**
         * 构造方法
         */
        public ZShaped() {
            cells = new Cell[4];

            cells[1] = new Cell(3, 0, Cell.COLOR_ORANGE);
            cells[2] = new Cell(4, 0, Cell.COLOR_ORANGE);
            cells[0] = new Cell(4, 1, Cell.COLOR_ORANGE);
            cells[3] = new Cell(5, 1, Cell.COLOR_ORANGE);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，L形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class LShaped extends Tetromino {
        /**
         * 构造方法
         */
        public LShaped() {
            cells = new Cell[4];

            cells[1] = new Cell(3, 0, Cell.COLOR_PINK);
            cells[0] = new Cell(4, 0, Cell.COLOR_PINK);
            cells[2] = new Cell(5, 0, Cell.COLOR_PINK);
            cells[3] = new Cell(3, 1, Cell.COLOR_PINK);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，O形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class OShaped extends Tetromino {
        /**
         * 构造方法
         */
        public OShaped() {
            cells = new Cell[4];

            cells[0] = new Cell(4, 0, Cell.COLOR_RED);
            cells[1] = new Cell(5, 0, Cell.COLOR_RED);
            cells[2] = new Cell(4, 1, Cell.COLOR_RED);
            cells[3] = new Cell(5, 1, Cell.COLOR_RED);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 内部类，J形的四格方块，继承了Tetromino类
     *
     * @author Leslie Leung
     */
    private class JShaped extends Tetromino {
        /**
         * 构造方法
         */
        public JShaped() {
            cells = new Cell[4];

            cells[1] = new Cell(3, 0, Cell.COLOR_YELLOW);
            cells[0] = new Cell(4, 0, Cell.COLOR_YELLOW);
            cells[2] = new Cell(5, 0, Cell.COLOR_YELLOW);
            cells[3] = new Cell(5, 1, Cell.COLOR_YELLOW);

            /* 设置旋转轴和要旋转的格子 */
            setAxis();
            setRotateCells();

            repaint();
        }
    }

    /**
     * 删除若干行
     */


    /**
     * 获取旋转轴
     *
     * @return 旋转轴
     */
    public Cell getAxis() {
        return currentTetromino.getAxis();
    }

    /**
     * 获取需要旋转的格子
     *
     * @return 需要旋转的格子
     */
    public Cell[] getRotateCells() {
        return currentTetromino.getRotateCells();
    }

    /**
     * 获取当前俄罗斯方块的所有格子
     *
     * @return 当前俄罗斯方块的所有格子
     */
    public Cell[] getCurrentCells() {
        return currentTetromino.getCells();
    }

    /**
     * 判断俄罗斯方块是否到达底部(包括是否到达面板底部和下一位置是否有方块)
     * @return true，到达；false，没到达
     */


    /**
     * 判断俄罗斯方块是否到达左边界(包括是否超出面板左边界和下一位置是否出现与其他方块碰撞)
     * @return true，已到达；false，没到达
     */


    /**
     * 判断俄罗斯方块是否到达右边界(包括是否超出面板右边界和下一位置是否出现与其他方块碰撞)
     * @return true，已到达；false，没到达
     */


    /**
     * 判断俄罗斯方块顺时针转后是否超出边界(包括是否超出面板边界和下一位置是否有方块)
     *
     * @return true，超出边界；false，没超边界
     */
    public boolean clockwiseRotateIsOutOfBounds() {
        int oldX;    //rotateCell的横坐标
        int oldY;    //rotateCell的纵坐标
        int newX;    //rotateCell旋转后的横坐标
        int newY;    //rotateCell旋转后的纵坐标

        for (int i = 0; i < 3; i++) {
            oldX = getRotateCells()[i].getX();
            oldY = getRotateCells()[i].getY();

            newX = getAxis().getX() - oldY + getAxis().getY();    //新横坐标计算算法
            newY = getAxis().getY() + oldX - getAxis().getX();    //新纵坐标计算算法

            if (newX < 0 || newY < 0 || newX > COLUMNS - 1 || newY > ROWS - 1) {//如果越界，返回true
                return true;
            }

            //检查是否旁边有其他方块
        }

        return false;
    }

    /**
     * 判断俄罗斯方块逆时针转后是否超出边界(包括是否超出面板边界和下一位置是否有方块)
     *
     * @return true，超出边界；false，没超边界
     */
    public boolean anticlockwiseRotateIsOutOfBounds() {
        for (int i = 0; i < 3; i++) {
            int oldX = getCurrentCells()[i].getX();
            int oldY = getCurrentCells()[i].getY();
            int newX = getAxis().getX() - getAxis().getY() + oldY;
            int newY = getAxis().getY() + getAxis().getX() - oldX;
            if (newX < 0 || newX > COLUMNS - 1 || newY < 0 || newY > ROWS - 1) {
                return true;
            }
            if (isHaveOther(newX, newY)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断某个格子是否存在方块对象
     * @param x 横坐标
     * @param y 纵坐标
     * @return true，存在对象；false，不存在对象
     */


    /**
     * 实现俄罗斯方块的硬下落
     */


    /**
     * 消除单行
     */
    public void clearRow(int rowNum) {
        //1、判断当前该行是否能够被删除
        for (int i = 0; i < COLUMNS; i++) {
            if (wall[rowNum][i] == null) {
                return;
            }
        }
        //2、删除行并将上面的所有方块向下移
        //wall[rowNum],wall[rowNum - 1]
        for (int i = rowNum; i >= 1; i--) {
            //1、源行，2、源行第一个拷贝列，3、目标列，4、目标行的第一复制列
            //5、拷贝数量
            System.arraycopy(wall[i - 1], 0, wall[i], 0, COLUMNS);
            for (int j = 0; j < COLUMNS; j++) {
                if (wall[i][j] != null) {
                    int oldY = wall[i][j].getY();
                    int newY = oldY + 1;
                    wall[i][j].setY(newY);
                }
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getBounds().width, getBounds().height);

        /* 画墙 */
        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLUMNS; j++) {
                if (wall[i][j] == null) {//某点的方块为空时
                    g.setColor(Color.WHITE);
                    g.fillRect(j * Cell.CELL_SIZE + 1, i * Cell.CELL_SIZE + 1, Cell.CELL_SIZE - 2, Cell.CELL_SIZE - 2);
                } else {//当方块不为空时
                    wall[i][j].paintCell(g);
                }
            }
        }

        /* 画当前俄罗斯方块 */
        for (int i = 0; i < getCurrentCells().length; i++) {
            getCurrentCells()[i].paintCell(g);
        }

    }
}
